﻿/*
 * Rappelz: Endless Odyssey - The first free open source Rappelz server emulator
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Rappelz.GameServer.Network.GameClient;

namespace Rappelz.GameServer.Network.AuthClient
{
    public class AuthClientNetwork : INetworkHandler
    {
        /// <summary>
        /// List of connected servers
        /// </summary>
        private static List<TCPConnection> m_clients = new List<TCPConnection>();
        private TCPConnection m_Connection;

        private int m_ConnectionCount = 0;
        private int m_DisconnectCount = 0;
        private int m_nRecvMessageCount = 0;
        private int m_nRecvMessageByte = 0;

        /// <summary>
        /// The instance!
        /// </summary>
        protected static AuthClientNetwork m_instance;

        /// <summary>
        /// Returns the instance
        /// </summary>
        public static AuthClientNetwork Instance
        {
            get { return m_instance; }
        }

        /// <summary>
        /// Returns the list of connected servers
        /// </summary>
        public static List<TCPConnection> Clients
        {
            get { return m_clients; }
        }

        /// <summary>
        /// Creates our GameServerNetwork instance
        /// </summary>
        public static void CreateInstance(TCPManager iocp)
        {
            if (m_instance == null)
                m_instance = new AuthClientNetwork(iocp);
        }

        /// <summary>
        /// Constructs an GameServerNetwork object
        /// </summary>
        protected AuthClientNetwork(TCPManager iocp)
        {
            m_Connection = new TCPConnection(iocp, null, this, Config.connAuthServer);
            //formLoginServer.Instance.UpdateClientStatus("Listening");
       }

        public bool Connect()
        {
            try
            {
                m_Connection.Start();
            }
            catch (System.Exception ex)
            {
                Globals.Log.Fatal("LoginNetwork Error: {0}", ex);
                return false;
            }

            PacketOut o = new PacketOut((ushort)ServerOutPackets.ServerLogin);
            o.WriteShort((ushort)Config.nIndex);
            o.FillString(Config.szServerName, 21);
            o.FillString(Config.szScreenshot, 256);
            o.WriteByte(0); // adult server
            o.FillString(Config.connAuthServer.SentIp.ToString(),16);
            o.WriteInt32(Config.srvGameClients.Port);

            m_Connection.SendTCP(o);
            return true;
        }

        /// <summary>
        /// Closes our server
        /// </summary>
        public void Close()
        {
            try
            {
                m_Connection.Close();
            }
            catch (System.Exception ex)
            {
                Globals.Log.Fatal("LoginNetwork Error: {0}", ex);
            }
        }


        public void onConnect(int nID, TCPConnection socket)
        {
            Interlocked.Increment(ref m_ConnectionCount);
        }

        public void onDisconnect(int nID, TCPConnection socket)
        {
            Interlocked.Increment(ref m_DisconnectCount);
        }

        public PacketIn ProcessPacket(TCPConnection socket, byte[] buf, int start, int size)
        {
//         ClientLoginResult = 20011,
//         KickClient = 20013,
//         ItemPurchased = 30000,
//         ItemSupplied = 30001,
//         SecurityNoCheck = 40000,

            ushort _id = Marshal.ConvertToUInt16(buf, start + 4);
            PacketIn _packet = null;
            switch (_id)
            {
                case (ushort)ServerInPackets.ServerLoginResult:
                    _packet = new PacketIn(buf, start, size);
                    ushort result = _packet.ReadUInt16(); // 0 = ok, 1 = error
                    break;

                case (ushort)ServerInPackets.ClientLoginResult:
                    _packet = new ClientLoginResultPacketAG(buf, start, size);
                    onClientLoginResult(socket, (ClientLoginResultPacketAG)_packet);
                    break;

                case (ushort)ServerInPackets.KickClient:
                    _packet = new KickClientPacketAG(buf, start, size);
                    onKickClient(socket, (KickClientPacketAG)_packet);
                    break;

/*
                case (ushort)ClientInPackets.ServerSelect:
                    _packet = new ServerSelectPacketCA(buf, start, size);
                    onServerSelect(socket, (ServerSelectPacketCA)_packet);

                    break;

                case (ushort)ClientInPackets.Packet9999:
                    _packet = new Unk9999PacketCA(buf, start, size);
                    break;
*/

                default:
                    _packet = new PacketIn(buf, start, size);
                    Globals.Log.Error("Received packet code is not a handled packet! " + socket);
                    Globals.Log.Error(Marshal.ToHexDump(
                                String.Format("===> <{2}> Packet 0x{0:X2} ({1}) length: {3} (ThreadId={4})", _packet.ID, _packet.ID,
                                              socket.TcpEndpoint,
                                              _packet.PacketSize - PacketIn.HDR_SIZE, System.Threading.Thread.CurrentThread.ManagedThreadId),
                                _packet.ToArray()));
                    break;

            }
            Interlocked.Increment(ref m_nRecvMessageCount);
            Interlocked.Add(ref m_nRecvMessageByte, (int)_packet.PacketSize);
            return _packet;
        }

        /// <summary>
        /// Sends a result packet to the auth server
        /// </summary>
        public void SendResult(TCPConnection client, ushort msg, ushort result, int value)
        {
            using (var pak = new PacketOut((ushort)0))
            {
                pak.WriteShort(msg);
                pak.WriteShort(result);
                pak.WriteInt32(value);
                pak.FinalizeLengthAndChecksum();
                client.SendTCP(pak);
            }
        }

        /// <summary>
        /// Sends a message to the auth server
        /// </summary>
        public void SendMessage(PacketOut packet)
        {
            if(m_Connection != null && m_Connection.Socket != null && m_Connection.Socket.Connected)
            {
                // _InterlockedExchangeAdd((signed __int32 *)&g_nSendMessageCount, 1u);
                // _InterlockedExchangeAdd((signed __int32 *)&g_nSendMessageByte, pMessage->size);
                // SetMessagingInfo(pMessage->id, pMessage->size, 1);
                m_Connection.SendTCP(packet);
            }
        }

/*
        /// <summary>
        /// Sends AuthComplete to the game client
        /// </summary>
        public static void SendAuthComplete(TCPConnection client, int nSuccessFlag)
        {
            ushort result = 1;// (ushort)AuroraServer.Instance.addAuthClient(client);

            Instance.SendResult(client, (ushort)10010, result, nSuccessFlag);
        }
*/

        /// <summary>
        /// Handles the ClientLoginResult Packet
        /// </summary>
        private void onClientLoginResult(TCPConnection client, ClientLoginResultPacketAG packet)
        {
            GameClientNetwork.Instance.onClientLoginResult(packet);
        }

        private void onKickClient(TCPConnection client, KickClientPacketAG packet)
        {
            string pn;
//            g_ConnectionTagLock.baseclass_0.vfptr->Lock(&g_ConnectionTagLock);
            TCPConnection conn = Player.GetConnectionByAccount(packet.szAccount);

            if (conn != null)
            {
                ConnectionTag tag = conn.Tag;
                Player player = tag.player;

                if (player != null)
                {
                    pn = player.GetName();
                    if (player.m_nLogoutTimer != 0)
                    {
                        Globals.Log.Info("AUTH DEBUG : KICK PLAYER FAILED [{0}/{1}] - Player({2}) is in world and waiting logout by logout-timer.",packet.szAccount, player.m_nLoginTime, pn);
                        if (player.priority < SchedulerObject.Priority.Normal && player.bIsInWorld)
                        {
                            RappelzServer.Instance.SetObjectPriority(player, SchedulerObject.Priority.Normal);
                        }
                    }
                    else
                    {
                        Globals.Log.Info("AUTH DEBUG : KICK PLAYER [%s/%d] : Player(%s) is led to logout by timer",packet.szAccount, player.m_nLoginTime, pn);
//                        *(*(v1 - 96) + 108) = player.GetContinuousPlayTime();
//                        *(*(v1 - 96) + 112) = player.GetContinuousLogoutTime();
                        uint nt = player.CanLogoutNow() ? 0 : GameRule.nLogoutTimer;
                        player.m_nLogoutTimer = nt + Globals.GetArTime();
                    }
                }
                if (conn.Socket.Connected)
                {
//                     v15 = v4[1].baseclass_0.baseclass_c.vfptr;
//                     *(v1 - 88) = v4[1].baseclass_0.baseclass_c.m_socket.vfptr;
//                     *(v1 - 92) = v4[1].baseclass_0.baseclass_c.m_socket.m_socket;
//                     *(v1 - 96) = *&v4[1].m_peerAddr.m_szAddr[124];
                    Messages.SendDisconnectDesc(conn, 1);//*(v2 + 68));
                    conn.Close();
//                    XConsole::Printf("AUTH DEBUG : KICK PLAYER [%s/%d] : Connection is closed. ConnectionInfo[%d(%d/%d),%d][%d(%d/%d),%d]\n");
                }
                else
                {
                    if (player == null)
                    {
//                        Globals.Log.Error("AUTH DEBUG : KICK PLAYER FAILED [%s/%d] : Logout is in process or unknown state. ConnectionInfo[%d(%d/%d),%d]\n");
                        if (GameRule.bForceUnregisterAccountOnKickFail)
                            Player.UnRegisterAccount(packet.szAccount);
                    }
                }
            }
            else
            {
                if (Player.IsRegisteredAccount(packet.szAccount))
                {
                    Globals.Log.Info("AUTH DEBUG : KICK PLAYER FAILED [{0}] - Maybe waiting logout by logout-timer.", packet.szAccount);
                }
                else
                {
                    Globals.Log.Info("AUTH DEBUG : KICK PLAYER FAILED [{0}] - Unregistered account.", packet.szAccount);

                    PacketOut po = new PacketOut((ushort)ServerOutPackets.ClientKickFail);
                    po.FillString(packet.szAccount,61);
                    this.m_Connection.SendTCP(po);
                }
            }
//            v30->UnLock(&g_ConnectionTagLock);
        }


        public void SendLogoutToAuth(string szAccount, uint nContinuousPlayTime, int nCallerIdx)
        {
            if (GameRule.bUseLoginLogoutDebug)
            {
                Globals.Log.Info("SendLogoutToAuth ({0}, {1})", szAccount, nCallerIdx);
            }

            PacketOut o = new PacketOut((ushort)ServerOutPackets.ClientLogout);
            o.FillString(szAccount, 61);
            o.WriteUInt32(nContinuousPlayTime);
            m_Connection.SendTCP(o);
        }
    }
}
